package demo.hystrix;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;

import demo.model.AccountResponseModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class AccountService {
	@Autowired
	private RestTemplate restTemplate;
	
	@HystrixCommand(commandKey="account-service-get-accounts" ,groupKey="account-service",threadPoolKey="account-service",fallbackMethod = "getAccountsFallback")
	public AccountResponseModel getAccounts(String name, String region) {
		log.debug("****getAccounts(\"{}\", \"{}\")*****",name,region);
		
		return  this.restTemplate.getForObject("http://account-service/account/list/{name}?region={region}", AccountResponseModel.class,name,region);
	}
	
	@SuppressWarnings("unused")
	private AccountResponseModel getAccountsFallback(String name, String region, Throwable t) {
		
		if (t.getCause() != null) {
			
			return new AccountResponseModel("account-service is unavailable for region[" + region + "][Exception:" + t.toString() + ", Cause:" + t.getCause().toString() + "]");
		}
		
		return new AccountResponseModel("account-service is unavailable for region[" + region + "][Exception:" + t.toString() + "]");
	}
	
	@CacheResult
	@HystrixCommand(commandKey="account-service-get-accounts-cache" ,groupKey="account-service",threadPoolKey="account-service",fallbackMethod = "getAccountsFallback")
	public AccountResponseModel getAccountsFromCache(String name, String region) {
		log.debug("****getAccounts(\"{}\", \"{}\")*****",name,region);
		
		return  this.restTemplate.getForObject("http://account-service/account/list/{name}?region={region}", AccountResponseModel.class,name,region);
	}
	
    @CacheRemove(commandKey="account-service-get-accounts-cache")
    @HystrixCommand
    public AccountResponseModel removeCache4GetAccounts(String name, String region) {             
        return null;
    }
	
	@CacheResult
	@HystrixCollapser(collapserKey="account-service-get-accounts-collapser", batchMethod = "getAccountsByBatch",scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = {
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "50"),
            @HystrixProperty(name = "maxRequestsInBatch", value = "10"),
        })
	public Future<AccountResponseModel> getAccounts(AccountServiceParameter param) {			   
		return null;
	}
		
	@HystrixCommand(commandKey="account-service-get-accounts-batch",groupKey="account-service",threadPoolKey="account-service",fallbackMethod = "getAccountsByBatchFallback")
	public List<AccountResponseModel> getAccountsByBatch(List<AccountServiceParameter> params) {			   
			
		ParameterizedTypeReference<List<AccountResponseModel>> responseType = new ParameterizedTypeReference<List<AccountResponseModel>>() {};
		
		List<String> names = new ArrayList<String>();
		List<String> regions = new ArrayList<String>();
		for(AccountServiceParameter param : params) {
			names.add(param.getName());
			regions.add(param.getRegion());
		}
		
		log.debug("***getAccountsByBatch: region:[{}]", StringUtils.join(regions,","));
		
		return restTemplate.exchange("http://account-service/account/batchlist/{names}?regions={regions}"
					,HttpMethod.GET
					, null
					, responseType
					,StringUtils.join(names,",")
					,StringUtils.join(regions,",")).getBody();	 	 
	}
	
	private List<AccountResponseModel> getAccountsByBatchFallback(List<AccountServiceParameter> params, Throwable t) {
		return Collections.emptyList();
	}
	
	@Data  @AllArgsConstructor @NoArgsConstructor
	public static class AccountServiceParameter{
		private String name;
		private String region;
	}
}
